<?php
/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
*/

/**
 * Description of VA_Form_FormElement
 *
 * @author chaporgin_a
 */
class VA_Form_FormElement {
    private $validators = array();
    private $decorators = array();
    private $_decLoaded = FALSE;
    private $label = null;
    private $name = null;
    protected $value = null;
    /**
     * Тег STYLE
     * @var VA_Form_Dictionary
     */
    private $style = null;
    private $form = null;
    private $errorMessages = array();
    private $viewHelper = 'textbox';
    public function __construct() {
        $this->style = new VA_Form_Dictionary();
    }
    /**
     * Массив аттрибутов для КАКИХ УГОДНО html тегов (решать использовать или нет -
     * дело за ViewHelper
     *
     * @var array
     */
    private $attributes = array();
    public function render() {
        $return = '';
        foreach($this->getValidators() as $validator)
            foreach($validator->getHTMLTagAttributes() as $name => $values)
                foreach($values as $value)
                    $this->setHTMLTagAttribute($name, $value);
        /* @var $decorator VA_Form_Decorator */
        foreach($this->getDecorators() as $decorator) {
            $decorator->setElement($this);
            $return = $decorator->render($return);
        }
        return $return;
    }
    /**
     * Returns array of decorators
     * @return array
     */
    protected function getDecorators() {
        if( !$this->_decLoaded) {
            if( empty ($this->decorators)) $this->setDefaultDecorators();
            foreach($this->decorators as $key => $options) $this->loadDecorator($key,$options);
            $this->_decLoaded = TRUE;
        }
        return $this->decorators;
    }
    protected function setDefaultDecorators() {
        $this->decorators = array();
        $this->addDecorator('ViewHelper')
                ->addDecorator('Error')
                ->addDecorator('HtmlTag', array('tag'=>'div', 'classes'=>array('element')))
                ->addDecorator('Label', array('position' => VA_Form_Decorator_Label::PREPEND))
                ->addDecorator('HtmlTag', array('tag'=>'div', 'jQueryMark'=>'controlWrapper', 'classes'=>array('elementWrapper')));
    }
    /**
     *
     * @param string $name
     * @param array $options
     * @return VA_Form_FormElement
     */
    public function addDecorator($name, $options = array()) {
        $this->decorators[] = array($name, $options);
        return $this;
    }
    /**
     * Loads decorator
     *
     * @param string $key
     * @return
     */
    private function loadDecorator($key, $init) {
        $this->decorators[$key] = VA_Form_Decorator::Generate($init[0], $init[1]);
    }
    /**
     *
     * @param string $label
     */
    public function setLabel($label) {
        $this->label = $label;
        return $this;
    }
    public function getLabel() {
        return $this->label;
    }
    /**
     *
     * @param VA_Form_Form $form
     * @return VA_Form_FormElement
     */
    public function setForm($form) {
        $this->form = $form;
        return $this;
    }
    /**
     *
     * @return VA_Form_Form
     */
    public function getForm() {
        return $this->form;
    }
    /**
     *
     * @param string $name
     */
    public function setName($name) {
        $this->name = $name;
        return $this;
    }
    public function getName() {
        return $this->name;
    }
    /**
     * Call all registered validators and find out!
     *
     * @return boolean
     */
    public function isValid() {
        $isValid = true;
        $value = $this->getValue();
        /* @var $chain VA_Form_Validator */
        foreach($this->getValidators() as $chain) {
            $res = $chain->isValid($value);
            if( FALSE === $res) {
                $isValid = FALSE;
                $messages = $chain->getErrorMessages();
                $this->addErrorMessages($messages);
                //}
            }
        }
        if(!$isValid)
            $this->markInvalid();

        return $isValid;
    }
    /**
     * Mark it somehow as invalid element
     */
    protected function markInvalid() {
        $dec = $this->getDecorators();
        /* @var $d VA_Form_Decorator_HtmlTag */
        $d = $dec[count($dec)-1];
        $d->addClass('error');
        $this->setHTMLTagAttribute('class', 'error');
        return $this;
    }

    /**
     *
     * @param mixed $value
     */
    public function setValue($value) {
        $this->value = $value;
        return $this;
    }
    public function getValue() {
        /*if(null === $this->value)
      throw new Exception('no value set');*/
        return $this->value;
    }
    /**
     *
     * @return string
     */
    public function getPrintableValue() {
        if(null === $this->value)
            return '';
        elseif (is_array($this->value))
            return join(', ', $this->value);
        else return $this->value;
    }
    /**
     *
     * @param array $message
     */
    private function addErrorMessages($message) {
        $this->errorMessages = array_merge($message, $this->errorMessages);
    }
    /**
     * Returns array of error messages
     * @return array
     */
    public function getErrorMessages() {
        return $this->errorMessages;
    }
    /**
     *
     * @return string
     */
    public function getViewHelper() {
        return ucfirst(strtolower($this->viewHelper));
    }
    /**
     *
     * @param string $name
     * @param string $value
     * @param boolean $replace - если TRUE то заменить все значения данного имени на $value
     * @return VA_Form_FormElement
     */
    public function setHTMLTagAttribute($name, $value, $replace = FALSE) {
        if( TRUE === $replace) {
            $this->attributes[$name] = array($value);
            return $this;
        }
        if(key_exists($name, $this->attributes)) {
            if(!in_array($value, $this->attributes[$name]))
                $this->attributes[$name][] = $value;
        } else {
            $this->attributes[$name] = array($value);
        }
        return $this;
    }
    /**
     *
     * @param string $name
     * @return string
     */
    public function getHTMLTagAttribute($name, $onlyValue = FALSE) {
        $this->setDefaultAttributes();
        if(key_exists($name, $this->attributes)) {
            $value = implode(' ', $this->attributes[$name]);
            if($onlyValue) return $value;
            else return $name.'="'.$value.'"';
        } else {
            switch($name) {
                case 'name':
                    $value = $this->getForm()->putIntoNamespace($this, VA_Form_Form::PUT_NAME);
                    break;
                case 'value':
                    $value = $this->getPrintableValue();
                    break;
                case 'style':
                    $value = $this->getStyle(TRUE);
                    break;
                default:
                    return '';
            }
            if($onlyValue) return $value;
            else return $name.'="'.$value.'"';
        }
        return '';
    }
    private function setDefaultAttributes() {
        $names = $this->getAttributeNames();
        if( !in_array('id', $names))
            $this->setHTMLTagAttribute('id', $this->getForm()->putIntoNamespace($this, VA_Form_Form::PUT_ID));

    }
    /**
     * Возвращает сразу все аттрибуты элемента, кроме таких сложных, как name, value,
     * в виде готовом для использования в тегах
     * @return string
     */
    public function getAllAttributesAsHTML() {
        $return = '';
        $this->setDefaultAttributes();
        foreach(array_keys($this->attributes) as $name)
            $return.=' '.$this->getHTMLTagAttribute($name);
        if( $this->getStyle(TRUE))
            $return.= ' '.$this->getStyle();
        $return .= ' '.$this->getHTMLTagAttribute('name');
        return trim($return.' ');
    }
    /**
     * Массив аттрибут - значение
     * @return array
     */
    public function getAttributeNames() {
        $return = array_keys($this->attributes);
        return $return;
    }
    /**
     *
     * @param VA_Form_Validator $validator
     * @return VA_Form_FormElement
     */
    public function addValidator(VA_Form_Validator $validator) {
        $this->validators[] = $validator;
        return $this;
    }
    /**
     *
     * @return array
     */
    public function getValidators() {
        return $this->validators;
    }
    public function setStyle($name, $value) {
        $this->style->Set($name, $value);
        return $this;
    }
    /**
     * Устаналивает стиль из строки типа "width: 200; height: 100px;" итд
     * @param string $singleLine
     */
    public function setStyleFromLine($singleLine) {
        $entries = explode(';', $singleLine);
        $l = count($entries);
        for($i=0;$i<$l;++$i) {
            $entry = $entries[$i];
            if(!empty($entry)) {
                list($name, $value) = explode(':', $entry);
                list($name, $value) = array(trim($name), trim($value));
                $this->setStyle($name, $value);
            }
        }
        return $this;
    }
    public function getStyle($onlyValue = FALSE) {
        $return = '';
        foreach($this->style->GetNames() as $name) {
            $return .= sprintf('%s: %s; ', $name, join(' ',$this->style->Get($name)));
        }
        if( $onlyValue) return trim($return);
        else return 'style="'.htmlentities(trim($return), ENT_COMPAT).'"';
    }
    /**
     * Возвращает один элемент атрибута style
     * @param string $name
     * @return mixed
     */
    public function getStyleElement($name) {
        return $this->style->Get($name);
    }
    public function ResetValue() {
        $this->value = '';
    }
    /**
     * Используется при частичной валидации
     * @return boolean
     */
    public function hasValue() {
        return !empty ($this->value);
    }
}
